//! Template system tests

use std::collections::HashMap;
use serde_json::json;
use comfyui_sdk::{
    WorkflowTemplate, TemplateManager, AI_MODEL_FACE_HAIR_FIX_TEMPLATE,
    ParameterType, ParameterSchema, TemplateMetadata, WorkflowTemplateData,
    ComfyUINode, NodeMeta
};

#[test]
fn test_template_creation() {
    let template_data = AI_MODEL_FACE_HAIR_FIX_TEMPLATE.clone();
    let template = WorkflowTemplate::from_data(template_data).unwrap();
    
    assert_eq!(template.id(), "ai-model-face-hair-fix");
    assert_eq!(template.name(), "AI Model Face & Hair Detail Fix");
    assert!(template.description().is_some());
    assert_eq!(template.category(), Some("enhancement"));
    assert!(!template.tags().is_empty());
}

#[test]
fn test_template_parameters() {
    let template_data = AI_MODEL_FACE_HAIR_FIX_TEMPLATE.clone();
    let template = WorkflowTemplate::from_data(template_data).unwrap();
    
    // Check required parameters
    let required_params = template.get_required_parameters();
    assert!(required_params.contains(&"input_image"));
    assert!(required_params.contains(&"face_prompt"));
    assert!(required_params.contains(&"face_denoise"));
    
    // Check parameter schemas
    let input_image_schema = template.get_parameter_schema("input_image").unwrap();
    assert_eq!(input_image_schema.param_type, ParameterType::String);
    assert_eq!(input_image_schema.required, Some(true));
    
    // Check default values
    let defaults = template.get_default_values();
    assert!(defaults.contains_key("input_image"));
    assert!(defaults.contains_key("face_prompt"));
    assert!(defaults.contains_key("face_denoise"));
}

#[test]
fn test_parameter_validation() {
    let template_data = AI_MODEL_FACE_HAIR_FIX_TEMPLATE.clone();
    let template = WorkflowTemplate::from_data(template_data).unwrap();
    
    // Valid parameters
    let mut valid_params = HashMap::new();
    valid_params.insert("input_image".to_string(), json!("test.jpg"));
    valid_params.insert("face_prompt".to_string(), json!("beautiful face"));
    valid_params.insert("face_denoise".to_string(), json!("0.3"));
    
    let validation = template.validate(&valid_params);
    assert!(validation.valid, "Valid parameters should pass validation");
    
    // Missing required parameter
    let mut invalid_params = HashMap::new();
    invalid_params.insert("face_prompt".to_string(), json!("test"));
    
    let validation = template.validate(&invalid_params);
    assert!(!validation.valid, "Missing required parameter should fail validation");
    assert!(!validation.errors.is_empty());
    
    // Unknown parameter
    let mut unknown_params = HashMap::new();
    unknown_params.insert("input_image".to_string(), json!("test.jpg"));
    unknown_params.insert("face_prompt".to_string(), json!("test"));
    unknown_params.insert("face_denoise".to_string(), json!("0.3"));
    unknown_params.insert("unknown_param".to_string(), json!("value"));
    
    let validation = template.validate(&unknown_params);
    assert!(!validation.valid, "Unknown parameter should fail validation");
}

#[test]
fn test_template_instance_creation() {
    let template_data = AI_MODEL_FACE_HAIR_FIX_TEMPLATE.clone();
    let template = WorkflowTemplate::from_data(template_data).unwrap();
    
    let mut params = HashMap::new();
    params.insert("input_image".to_string(), json!("test.jpg"));
    params.insert("face_prompt".to_string(), json!("beautiful face"));
    params.insert("face_denoise".to_string(), json!("0.3"));
    
    let instance = template.create_instance(params).unwrap();
    
    assert_eq!(instance.template_id(), "ai-model-face-hair-fix");
    assert_eq!(instance.template_name(), "AI Model Face & Hair Detail Fix");
    assert!(instance.node_count() > 0);
    
    // Check that parameters were applied
    assert_eq!(instance.get_parameter("input_image"), Some(&json!("test.jpg")));
    assert_eq!(instance.get_parameter("face_prompt"), Some(&json!("beautiful face")));
    assert_eq!(instance.get_parameter("face_denoise"), Some(&json!("0.3")));
}

#[test]
fn test_template_manager() {
    let mut manager = TemplateManager::new();
    
    // Initially empty
    assert_eq!(manager.count(), 0);
    assert!(manager.list_ids().is_empty());
    
    // Register template
    let template_data = AI_MODEL_FACE_HAIR_FIX_TEMPLATE.clone();
    manager.register_from_data(template_data).unwrap();
    
    assert_eq!(manager.count(), 1);
    assert!(manager.contains("ai-model-face-hair-fix"));
    
    // Get template
    let template = manager.get_by_id("ai-model-face-hair-fix").unwrap();
    assert_eq!(template.id(), "ai-model-face-hair-fix");
    
    // Search by category
    let enhancement_templates = manager.get_by_category("enhancement");
    assert_eq!(enhancement_templates.len(), 1);
    
    // Search by tag
    let face_templates = manager.get_by_tag("face-enhancement");
    assert_eq!(face_templates.len(), 1);
    
    // Search by name
    let search_results = manager.search_by_name("Face");
    assert_eq!(search_results.len(), 1);
}

#[test]
fn test_custom_template_creation() {
    // Create a simple custom template
    let metadata = TemplateMetadata {
        id: "test-template".to_string(),
        name: "Test Template".to_string(),
        description: Some("A test template".to_string()),
        version: Some("1.0.0".to_string()),
        author: Some("Test Author".to_string()),
        tags: Some(vec!["test".to_string()]),
        category: Some("test".to_string()),
        created_at: None,
        updated_at: None,
    };
    
    let mut parameters = HashMap::new();
    parameters.insert("test_param".to_string(), ParameterSchema {
        param_type: ParameterType::String,
        required: Some(true),
        default: Some(json!("default_value")),
        description: Some("A test parameter".to_string()),
        r#enum: None,
        min: None,
        max: None,
        pattern: None,
        items: None,
        properties: None,
    });
    
    let mut workflow = HashMap::new();
    workflow.insert("1".to_string(), ComfyUINode {
        class_type: "TestNode".to_string(),
        inputs: {
            let mut inputs = HashMap::new();
            inputs.insert("input".to_string(), json!("{{test_param}}"));
            inputs
        },
        _meta: Some(NodeMeta {
            title: Some("Test Node".to_string()),
        }),
    });
    
    let template_data = WorkflowTemplateData {
        metadata,
        workflow,
        parameters,
    };
    
    let template = WorkflowTemplate::from_data(template_data).unwrap();
    
    assert_eq!(template.id(), "test-template");
    assert_eq!(template.name(), "Test Template");
    assert!(template.has_parameter("test_param"));
    
    // Test instance creation with parameter substitution
    let mut params = HashMap::new();
    params.insert("test_param".to_string(), json!("test_value"));
    
    let instance = template.create_instance(params).unwrap();
    let workflow = instance.workflow();
    
    // Check that the parameter was substituted in the workflow
    let node = workflow.get("1").unwrap();
    let input_value = node.inputs.get("input").unwrap();
    assert_eq!(input_value, &json!("test_value"));
}

#[test]
fn test_template_validation_errors() {
    // Test template with invalid metadata
    let invalid_metadata = TemplateMetadata {
        id: "".to_string(), // Empty ID should fail
        name: "Test".to_string(),
        description: None,
        version: None,
        author: None,
        tags: None,
        category: None,
        created_at: None,
        updated_at: None,
    };
    
    let template_data = WorkflowTemplateData {
        metadata: invalid_metadata,
        workflow: HashMap::new(),
        parameters: HashMap::new(),
    };
    
    let result = WorkflowTemplate::from_data(template_data);
    assert!(result.is_err(), "Template with empty ID should fail validation");
}

#[test]
fn test_template_serialization() {
    let template_data = AI_MODEL_FACE_HAIR_FIX_TEMPLATE.clone();
    let template = WorkflowTemplate::from_data(template_data.clone()).unwrap();
    
    // Convert back to data
    let serialized_data = template.to_data();
    
    // Should be equivalent to original
    assert_eq!(serialized_data.metadata.id, template_data.metadata.id);
    assert_eq!(serialized_data.metadata.name, template_data.metadata.name);
    assert_eq!(serialized_data.parameters.len(), template_data.parameters.len());
    assert_eq!(serialized_data.workflow.len(), template_data.workflow.len());
}
